using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.Composition;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml.Serialization;
using SMSManager.Commn.Interfaces;
using SMSManager.Data.Properties;

namespace SMSManager.Data
{
	[Export(typeof (ISMSInfo))]
	[PartCreationPolicy(CreationPolicy.NonShared)]
	[TypeConverter(typeof (XmlTypeConverter<SMSInfo>))]
	public class SMSInfo : ISMSInfo
	{
		private string _message;

		public SMSInfo()
		{
			CharCount = 0;
			_message = "";
			Receiver = "";
			Sender = "";

			CharPerMessage = 90;
			MessageCount = 0;
			MessageSet = new string[0];
		}

		private void SetMessage(string message)
		{
			CharCount = GetMessageCount(message);
			if (CharCount == 0)
			{
				CharPerMessage = 90;
				MessageCount = 0;
				MessageSet = new string[MessageCount];
			}
			else if (CharCount <= 90)
			{
				CharPerMessage = 90;
				MessageCount = 1;
				MessageSet = new string[MessageCount];
				MessageSet[0] = message;
			}
			else
			{
				List<byte> bytes = InsertBlankAtBoundary(message);
				CharCount = bytes.Count; //CharCount is updated.
				CharPerMessage = 85;

				MessageCount = (CharCount/CharPerMessage) + (CharCount%CharPerMessage == 0 ? 0 : 1);
				MessageSet = new string[MessageCount];
				SetMessageSet(bytes);
			}
		}

		private void SetMessageSet(List<byte> bytes)
		{
			for (int i = 0; i < MessageCount; i++)
			{
				int start = i*CharPerMessage;
				int end = (i + 1)*CharPerMessage;

				List<byte> subBytes = bytes.GetRange(start, Math.Min(end, bytes.Count) - start);
				string message = Encoding.GetEncoding("euc-kr").GetString(subBytes.ToArray());
				MessageSet[i] = string.Format("({0}/{1}){2}", i + 1, MessageCount, message);
			}
			Array.Reverse(MessageSet);
		}

		[XmlIgnore]
		public string this[string propertyName]
		{
			get
			{
				switch (propertyName)
				{
					case "Receiver":
						bool valid = Regex.IsMatch(Receiver, Resources.ReceiverRegex);
						if (!valid) return Resources.ReceiverInvalidMessage;
						break;
					case "Sender":
						valid = Regex.IsMatch(Sender, Resources.SenderRegex);
						if (!valid) return Resources.SenderInvalidMessage;
						break;
					case "Message":
						valid = Regex.IsMatch(Message, @"^.+");
						if (!valid) return "Message should not be empty.";
						break;
				}
				return null;
			}
		}

		[XmlIgnore]
		public int CharPerMessage { get; private set; }

		[XmlIgnore]
		public string Error
		{
			get { throw new NotSupportedException(); }
		}

		[XmlElement]
		public string Message
		{
			get { return _message; }
			set
			{
				SetMessage(value);
				_message = value;
			}
		}

		[XmlIgnore]
		public int CharCount { get; private set; }

		[XmlIgnore]
		public string[] MessageSet { get; private set; }

		[XmlIgnore]
		public int MessageCount { get; private set; }

		[XmlElement]
		public string Receiver { get; set; }

		[XmlElement]
		public string Sender { get; set; }

		private static List<bool> CreateFirstByteOfUnicodeList(string message)
		{
			var isFirstByteOfUnicodeList = new List<bool>();
			foreach (char ch in message)
			{
				if (ch <= 255)
				{
					isFirstByteOfUnicodeList.Add(false);
				}
				else
				{
					isFirstByteOfUnicodeList.Add(true);
					isFirstByteOfUnicodeList.Add(false);
				}
			}
			return isFirstByteOfUnicodeList;
		}

		private static int GetMessageCount(string message)
		{
			int messageCount = 0;
			foreach (char ch in message)
			{
				if (ch > 256) messageCount += 2;
				else messageCount++;
			}
			return messageCount;
		}

		private static List<byte> InsertBlankAtBoundary(string message)
		{
			List<bool> isFirstByteOfUnicodeList = CreateFirstByteOfUnicodeList(message);

			Encoding encoding = Encoding.GetEncoding("euc-kr");
			var bytes = new List<byte>(encoding.GetBytes(message));
			for (int i = 84; i < bytes.Count; i += 85)
			{
				//boundary is unicode?
				if (isFirstByteOfUnicodeList[i])
				{
					//insert blank (ascii code 32);
					bytes.Insert(i, 32);
					isFirstByteOfUnicodeList.Insert(i, false);
				}
			}
			return bytes;
		}
	}
}